package io.hellsinger.vortex.ui.screen.storage.transaction

import io.hellsinger.filesystem.linux.directory.asLinuxDirectory
import io.hellsinger.filesystem.linux.directory.size
import io.hellsinger.filesystem.linux.toIntMode
import io.hellsinger.filesystem.size.SiSize
import io.hellsinger.viewmodel.VortexViewModel
import io.hellsinger.vortex.Dispatchers
import io.hellsinger.vortex.data.model.PathItem
import io.hellsinger.vortex.data.model.name
import io.hellsinger.vortex.domain.repository.StorageRepository
import io.hellsinger.vortex.service.model.asDest
import io.hellsinger.vortex.service.model.asSource
import io.hellsinger.vortex.service.storage.TransactionController
import io.hellsinger.vortex.service.storage.transaction.CopyTransaction
import io.hellsinger.vortex.service.storage.transaction.CreateTransaction
import io.hellsinger.vortex.service.storage.transaction.DeleteTransaction
import io.hellsinger.vortex.service.storage.transaction.MoveTransaction
import io.hellsinger.vortex.service.storage.transaction.RenameTransaction
import io.hellsinger.vortex.service.storage.transaction.StorageTransaction
import io.hellsinger.vortex.ui.component.adapter.title
import io.hellsinger.vortex.ui.component.adapter.twoLine
import io.hellsinger.vortex.ui.screen.storage.transaction.StorageTransactionScreenController.Args.Companion.LOAD_TYPE_COPY
import io.hellsinger.vortex.ui.screen.storage.transaction.StorageTransactionScreenController.Args.Companion.LOAD_TYPE_CREATE
import io.hellsinger.vortex.ui.screen.storage.transaction.StorageTransactionScreenController.Args.Companion.LOAD_TYPE_DELETE
import io.hellsinger.vortex.ui.screen.storage.transaction.StorageTransactionScreenController.Args.Companion.LOAD_TYPE_MOVE
import io.hellsinger.vortex.ui.screen.storage.transaction.StorageTransactionScreenController.Args.Companion.LOAD_TYPE_RENAME
import io.hellsinger.vortex.ui.screen.storage.transaction.StorageTransactionScreenController.Args.Modification
import io.hellsinger.vortex.ui.screen.storage.transaction.StorageTransactionScreenController.Args.Transition
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.time.Duration.Companion.seconds

class StorageTransactionViewModel(
    private val dispatchers: Dispatchers,
    private val repository: StorageRepository,
) : VortexViewModel<UiState, MutableUiState>(scopeName = "Storage Transaction Scope") {
    override val state: MutableUiState = MutableUiState()
    private var controller: TransactionController? = null
    private var serviceCollecting: Job? = null

    override fun getUiState(): UiState = state

    fun buildChanges(args: StorageTransactionScreenController.Args) {
        val type = args.type
        when (type) {
            LOAD_TYPE_CREATE -> if (args is Modification) buildCreateChanges(args.items.single())
            LOAD_TYPE_DELETE -> if (args is Modification) buildDeleteChanges(args.items)
            LOAD_TYPE_COPY -> if (args is Transition) buildCopyChanges(args.items, args.dest)
            LOAD_TYPE_MOVE -> if (args is Transition) buildMoveChanges(args.items, args.dest)
            LOAD_TYPE_RENAME ->
                if (args is Transition) {
                    buildRenameChanges(
                        args.items.single(),
                        args.dest,
                    )
                }
        }
    }

    private fun buildCreateChanges(item: PathItem) =
        intent { state ->
            val loading = launchLoading()
            state.updateItems {
                title("Before")
                twoLine("Name", "Unreserved")
                twoLine("Size", "Unallocated")
                title("After")
                twoLine("Name", item.name)
                twoLine("Type", if (item.isDirectory) "Directory" else "File")
                twoLine("Permission", item.permission)
                twoLine("Size", "${SiSize(item.attrs.size)}")
                title("This operation is reversible")
                twoLine("Estimated time", "Less than second")

                loading.cancel()
                update(UPDATE_INFO)
            }
        }

    private fun buildDeleteChanges(items: List<PathItem>) =
        intent { state ->
            val loading = launchLoading()
            state.updateItems {
                val size =
                    withContext(dispatchers.io) {
                        items.fold(initial = 0L) { acc, item ->
                            if (item.isDirectory) {
                                acc + item.asLinuxDirectory().size()
                            } else {
                                item.attrs.size + acc
                            }
                        }
                    }

                title("Before")
                if (items.size > 1) {
                    twoLine("Names", items.take(5).joinToString { item -> item.name })
                } else {
                    twoLine("Name", items.single().name)
                }

                twoLine("Size", "${SiSize(size)}")
                title("After")
                twoLine(if (items.size > 1) "Names" else "Name", "Removed from file system")
                twoLine("Size", "Wiped")
                title("This operation is not reversible")

                val time = 1.seconds * items.size
                twoLine("Estimated time", time.toString())

                loading.cancel()
                update(UPDATE_INFO)
            }
        }

    private fun buildCopyChanges(
        items: List<PathItem>,
        dest: PathItem,
    ) = intent { state ->
        val loading = launchLoading()
        state.updateItems {
            val size =
                withContext(dispatchers.io) {
                    items.fold(initial = 0L) { acc, item ->
                        if (item.isDirectory) {
                            acc + item.asLinuxDirectory().size()
                        } else {
                            item.attrs.size + acc
                        }
                    }
                }

            title("Before")
            if (items.size > 1) {
                twoLine("Names", items.take(5).joinToString { item -> item.name })
            } else {
                twoLine("Name", items.single().name)
            }

            title("After")
            if (items.size > 1) {
                twoLine("Names", "Copied to ${dest.name}")
            } else {
                twoLine("Name", "Copied to ${dest.name}")
            }
            twoLine("Size", "${SiSize(size)} will be copied to destination")

            loading.cancel()
            update(UPDATE_INFO)
        }
    }

    private fun buildMoveChanges(
        items: List<PathItem>,
        dest: PathItem,
    ) = intent { state ->
        val loading = launchLoading()
        state.updateItems {
            val size =
                withContext(dispatchers.io) {
                    items.fold(initial = 0L) { acc, item ->
                        if (item.isDirectory) {
                            acc + item.asLinuxDirectory().size()
                        } else {
                            item.attrs.size + acc
                        }
                    }
                }

            title("Before")
            if (items.size > 1) {
                twoLine("Names", items.take(5).joinToString { item -> item.name })
            } else {
                twoLine("Name", items.single().name)
            }

            title("After")
            if (items.size > 1) {
                twoLine("Names", "Moved to ${dest.name}")
            } else {
                twoLine("Name", "Moved to ${dest.name}")
            }
            twoLine("Size", "${SiSize(size)} will be moved to destination")

            loading.cancel()
            update(UPDATE_INFO)
        }
    }

    private fun buildRenameChanges(
        item: PathItem,
        dest: PathItem,
    ) = intent { state ->
        val loading = launchLoading()

        state.updateItems {
            title("Before")
            twoLine("Name", item.name)

            title("After")
            twoLine("Name", "Renamed to ${dest.name}")

            loading.cancel()
            update(UPDATE_INFO)
        }
    }

    private fun launchLoading(timeMillis: Long = 1000L) =
        viewModelScope.launch {
            delay(timeMillis)
            update(UPDATE_LOADING)
        }

    fun confirm(
        args: StorageTransactionScreenController.Args,
        onResult: () -> Unit,
    ) = intent {
        val transaction = getTransaction(args) ?: return@intent
        controller?.registerTransaction(transaction)?.run() ?: return@intent
        onResult()
    }

    private fun getTransaction(args: StorageTransactionScreenController.Args): StorageTransaction? =
        when (args.type) {
            LOAD_TYPE_CREATE ->
                if (args is Modification) {
                    val item = args.items.single()
                    CreateTransaction(
                        dest = item.asDest(),
                        mode = item.permission.toIntMode(),
                        isDirectory = item.isDirectory,
                    )
                } else {
                    null
                }

            LOAD_TYPE_DELETE ->
                if (args is Modification) {
                    DeleteTransaction(sources = args.items.map(PathItem::asSource))
                } else {
                    null
                }

            LOAD_TYPE_MOVE ->
                if (args is Transition) {
                    MoveTransaction(
                        sources = args.items.map(PathItem::asSource),
                        dest = args.dest.asDest(),
                    )
                } else {
                    null
                }

            LOAD_TYPE_COPY ->
                if (args is Transition) {
                    CopyTransaction(
                        sources = args.items.map(PathItem::asSource),
                        dest = args.dest.asDest(),
                    )
                } else {
                    null
                }

            LOAD_TYPE_RENAME ->
                if (args is Transition) {
                    RenameTransaction(
                        source = args.items.single().asSource(),
                        dest = args.dest.asDest(),
                    )
                } else {
                    null
                }

            else -> null
        }

    fun collectServiceState(flow: Flow<TransactionController?>) {
        serviceCollecting =
            intent {
                flow.collect { controller ->
                    this@StorageTransactionViewModel.controller = controller
                }
            }
    }

    fun cancelServiceCollecting() {
        serviceCollecting?.cancel()
        controller = null
    }

    override fun onDestroy() {
        super.onDestroy()
        cancelServiceCollecting()
    }

    companion object {
        const val UPDATE_INFO = 0
        const val UPDATE_LOADING = 1 shl 0
        const val UPDATE_LOADING_VALIDATE = 1 shl 1
    }
}
